﻿using System;
using System.Collections.Generic;
using StackExchange.Redis;
using WeDonekRpc.CacheClient.Batch;
using WeDonekRpc.CacheClient.Interface;
using WeDonekRpc.Helper;

namespace WeDonekRpc.CacheClient.Redis
{
    public class RedisCache : IRedisCache
    {
        public bool IsEnable
        {
            get => RpcCacheService.CheckIsInit(this.CacheType);
        }
        protected IDatabase DataBase
        {
            get
            {
                return RedisHelper.RedisClient.DataBase;
            }
        }
        public string FormatKey (string key)
        {
            return RpcCacheService.FormatKey(key);
        }

        public CacheType CacheType { get; }
        public bool IsConnected => RedisHelper.RedisClient.IsConnected;

        public RedisCache ()
        {
            this.CacheType = CacheType.Redis;
        }
        public IRedisBatchWrite CreateBatch (object asyncObject = null)
        {
            return new RedisBatchWrite(this.DataBase.CreateBatch(asyncObject));
        }
        public byte[] CacheLua (string lua)
        {
            return RedisHelper.CacheLua(lua);
        }
        public T[] Gets<T> (string[] keys)
        {
            if (keys.Length == 1)
            {
                return keys.ConvertAll(c => SyncRedisHelper.Get<T>(this.DataBase, c));
            }
            return SyncRedisHelper.Gets<T>(this.DataBase, keys);
        }
        public Dictionary<string, T> GetKeyValue<T> (string[] keys)
        {
            return SyncRedisHelper.GetKeyValue<T>(this.DataBase, keys);
        }
        public T AddOrUpdate<T> (string key, T data, Func<T, T, T> upFunc, TimeSpan? expires = null)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, out T res))
            {
                data = upFunc(res, data);
                if (data == null || data.Equals(res))
                {
                    return res;
                }
            }
            return SyncRedisHelper.Set<T>(this.DataBase, key, data, expires) ? data : res;
        }

        public Result ExecLua<T, Result> (string lua, T arg) where T : class
        {
            return SyncRedisHelper.ExecLua<T, Result>(this.DataBase, lua, arg);
        }

        public Result ExecLua<Result> (string lua, RedisKey[] keys, RedisValue[] args)
        {
            return SyncRedisHelper.ExecLua<Result>(this.DataBase, lua, keys, args);
        }
        public void ExecLua (byte[] luaSha, RedisKey[] keys, RedisValue[] args)
        {
            SyncRedisHelper.ExecLua(this.DataBase, luaSha, keys, args);
        }
        public Result ExecLua<Result> (byte[] luaSha, RedisKey[] keys, RedisValue[] args)
        {
            return SyncRedisHelper.ExecLua<Result>(this.DataBase, luaSha, keys, args);
        }
        public T GetOrAdd<T> (string key, T data, TimeSpan? expires = null)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, out T obj))
            {
                return obj;
            }
            else if (SyncRedisHelper.Set<T>(this.DataBase, key, data, expires))
            {
                return data;
            }
            else
            {
                return SyncRedisHelper.TryGet<T>(this.DataBase, key, out obj) ? obj : default;
            }
        }

        public bool Remove (string key)
        {
            return SyncRedisHelper.Remove(this.DataBase, key);
        }


        public bool Set<T> (string key, T data, TimeSpan? expires = null)
        {
            return SyncRedisHelper.Set<T>(this.DataBase, key, data, expires);
        }
        public bool Set<T> (string key, T data, DateTime expires)
        {
            return SyncRedisHelper.Set<T>(this.DataBase, key, data, expires);
        }
        public bool TryGet<T> (string key, out T data)
        {
            return SyncRedisHelper.TryGet(this.DataBase, key, out data);
        }
        public bool TryRemove<T> (string key, Func<T, bool> func, out T data)
        {
            if (!SyncRedisHelper.TryGet(this.DataBase, key, out data))
            {
                return false;
            }
            else if (!func(data))
            {
                return true;
            }
            return SyncRedisHelper.Remove(this.DataBase, key);
        }
        public bool TryRemove<T> (string key, out T data)
        {
            return SyncRedisHelper.TryGet(this.DataBase, key, out data) && SyncRedisHelper.Remove(this.DataBase, key);
        }

        public bool Replace<T> (string key, T data, TimeSpan? expires = null)
        {
            return SyncRedisHelper.Replace<T>(this.DataBase, key, data, expires);
        }

        public bool Add<T> (string key, T data, TimeSpan? expires = null)
        {
            return SyncRedisHelper.Add<T>(this.DataBase, key, data, expires);
        }

        public bool TryUpdate<T> (string key, Func<T, T> upFunc, out T data, TimeSpan? expires = null)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, out data))
            {
                T up = upFunc(data);
                if (up == null || up.Equals(data))
                {
                    return true;
                }
                data = up;
            }
            else
            {
                return false;
            }
            return SyncRedisHelper.Replace<T>(this.DataBase, key, data, expires);
        }

        public T TryUpdate<T> (string key, T data, Func<T, T, T> upFunc, TimeSpan? expires = null)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, out T res))
            {
                data = upFunc(res, data);
                if (data == null || data.Equals(res))
                {
                    return res;
                }
            }
            else
            {
                return default;
            }
            return SyncRedisHelper.Replace<T>(this.DataBase, key, data, expires) ? data : res;
        }

        public bool Add<T> (string key, string colname, T data)
        {
            return SyncRedisHelper.Add<T>(this.DataBase, key, colname, data);
        }
        public bool Set<T> (string key, string colname, T data)
        {
            return SyncRedisHelper.Set<T>(this.DataBase, key, colname, data);
        }
        public bool Replace<T> (string key, string colname, T data)
        {
            return SyncRedisHelper.Replace<T>(this.DataBase, key, colname, data);
        }
        public bool Remove (string key, string colname)
        {
            return SyncRedisHelper.Remove(this.DataBase, key, colname);
        }

        public bool TryGet<T> (string key, string colname, out T data)
        {
            return SyncRedisHelper.TryGet(this.DataBase, key, colname, out data);
        }

        public bool TryGet<T> (string key, out T[] data)
        {
            return SyncRedisHelper.TryGet(this.DataBase, key, out data);
        }

        public long Remove (string key, string[] colname)
        {
            return SyncRedisHelper.Remove(this.DataBase, key, colname);
        }

        public string[] FindKey (string pattern)
        {
            return SyncRedisHelper.FindKey(this.DataBase, pattern);
        }

        public long GetCount (string key)
        {
            return SyncRedisHelper.GetCount(this.DataBase, key);
        }

        public bool TryGetColName (string key, out string[] colname)
        {
            return SyncRedisHelper.TryGetColName(this.DataBase, key, out colname);
        }

        public long Remove (string[] key)
        {
            return SyncRedisHelper.Remove(this.DataBase, key);
        }

        public bool IsExitKey (string key, string colName)
        {
            return SyncRedisHelper.IsExitKey(this.DataBase, key, colName);
        }

        public bool IsExitKey (string key)
        {
            return SyncRedisHelper.IsExitKey(this.DataBase, key);
        }
        public bool SetExpire (string key, DateTime time)
        {
            return SyncRedisHelper.SetExpire(this.DataBase, key, time);
        }

        public void Dispose ()
        {
        }

        public T AddOrUpdate<T> (string key, string column, T data, Func<T, T, T> upFunc)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, column, out T res))
            {
                data = upFunc(res, data);
                if (data == null || data.Equals(res))
                {
                    return res;
                }
            }
            return SyncRedisHelper.Set<T>(this.DataBase, key, column, data) ? data : default;
        }

        public T GetOrAdd<T> (string key, string column, T data)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, column, out T obj))
            {
                return obj;
            }
            else
            {
                return SyncRedisHelper.Set<T>(this.DataBase, key, column, data)
                        ? data
                        : SyncRedisHelper.TryGet<T>(this.DataBase, key, column, out obj) ? obj : default;
            }
        }

        public bool TryRemove<T> (string key, string column, out T data)
        {
            if (!SyncRedisHelper.TryGet(this.DataBase, key, column, out data))
            {
                return false;
            }
            return SyncRedisHelper.Remove(this.DataBase, key, column);
        }

        public T TryUpdate<T> (string key, string column, T data, Func<T, T, T> upFunc)
        {
            if (SyncRedisHelper.TryGet<T>(this.DataBase, key, column, out T res))
            {
                data = upFunc(res, data);
                if (data == null || data.Equals(res))
                {
                    return res;
                }
            }
            else
            {
                return default;
            }
            return SyncRedisHelper.Replace<T>(this.DataBase, key, column, data) ? data : res;
        }

        #region 递增和递减
        public double Increment (string key, double num)
        {

            return SyncRedisHelper.Increment(this.DataBase, key, num);
        }
        public long Increment (string key, long num)
        {

            return SyncRedisHelper.Increment(this.DataBase, key, num);
        }
        public double Decrement (string key, double num)
        {

            return SyncRedisHelper.Decrement(this.DataBase, key, num);
        }
        public long Decrement (string key, long num)
        {

            return SyncRedisHelper.Decrement(this.DataBase, key, num);
        }
        public double Increment (string key, string colname, double num)
        {

            return SyncRedisHelper.Increment(this.DataBase, key, colname, num);
        }

        public long Increment (string key, string colname, long num)
        {

            return SyncRedisHelper.Increment(this.DataBase, key, colname, num);
        }

        public double Decrement (string key, string colname, double num)
        {

            return SyncRedisHelper.Decrement(this.DataBase, key, colname, num);
        }

        public long Decrement (string key, string colname, long num)
        {

            return SyncRedisHelper.Decrement(this.DataBase, key, colname, num);
        }
        #endregion


        #region List
        public long ListInsert<T> (string key, T[] data)
        {

            return SyncRedisHelper.ListAddLeft<T>(this.DataBase, key, data);
        }
        public long ListInsert<T> (string key, T data)
        {

            return SyncRedisHelper.ListAddLeft<T>(this.DataBase, key, data);
        }
        public long ListRemove<T> (string key, T data)
        {

            return SyncRedisHelper.ListRemove<T>(this.DataBase, key, data);
        }
        public bool GetList<T> (string key, int index, int size, out T[] data)
        {

            return SyncRedisHelper.GetList<T>(this.DataBase, key, index, size, out data);
        }
        public long GetListCount (string key)
        {

            return SyncRedisHelper.GetListCount(this.DataBase, key);
        }
        public int AddTopList<T> (string key, T data, int top)
        {

            long num = SyncRedisHelper.ListAddLeft<T>(this.DataBase, key, data);
            if (num > top)
            {
                SyncRedisHelper.ListTop(this.DataBase, key, top);
                return top;
            }
            return (int)num;
        }
        public int AddTopList<T> (string key, T data, int start, int end)
        {

            long num = SyncRedisHelper.ListAddLeft<T>(this.DataBase, key, data);
            if (num > end)
            {
                SyncRedisHelper.ListTop(this.DataBase, key, start, end);
                return end - start;
            }
            return (int)num;
        }

        public T TryUpdateList<T> (string key, int index, T data)
        {

            if (!SyncRedisHelper.GetList<T>(this.DataBase, key, index, out T res))
            {
                return res;
            }
            SyncRedisHelper.SetList<T>(this.DataBase, key, index, data);
            return data;
        }
        public long ListAppend<T> (string key, T[] data)
        {

            return SyncRedisHelper.ListAddRight<T>(this.DataBase, key, data);
        }

        public long ListAppend<T> (string key, T data)
        {
            return SyncRedisHelper.ListAddRight<T>(this.DataBase, key, data);
        }

        public bool GetList<T> (string key, int index, out T data)
        {

            return SyncRedisHelper.GetList<T>(this.DataBase, key, index, out data);
        }
        public bool GetList<T> (string key, out T[] data)
        {

            return SyncRedisHelper.GetList<T>(this.DataBase, key, out data);
        }

        public bool TryRemove<T> (string key, Func<T, bool> func)
        {
            if (!SyncRedisHelper.TryGet(this.DataBase, key, out T data))
            {
                return false;
            }
            else if (!func(data))
            {
                return true;
            }
            return SyncRedisHelper.Remove(this.DataBase, key);
        }


        #endregion
    }
}
